#include "file_protocol_engine.h"

#include "types.h"
#include "logger.h"

const std::string CFileProtocolEngine::sIniSectionName("FPE");
const std::string CFileProtocolEngine::sDefaultReadFilename = "/tmp/read.dat";
const std::string CFileProtocolEngine::sDefaultWriteFilename = "/tmp/write.dat";

CFileProtocolEngine::CFileProtocolEngine(CPropertiesReader* pConfig) :
	mConfig(pConfig),
	mIOFilenameRead(sDefaultReadFilename),
	mIOFilenameWrite(sDefaultWriteFilename),
	mSendEof(true),
	mBytesToRead(0),
	mBytesToReceive(-1),
	mAvailableReadBytes(0), // for available() methode
	mLengthCntRead(0),
	mLengthCntWrite(0)
{
	std::string lString;
  if (pConfig->containsKey(sIniSectionName, "READ_FILE")) 
	{
    if (pConfig->getValue(
          sIniSectionName, "READ_FILE", lString)) 
		{
			mIOFilenameRead = lString;
    }   
  }
  if (pConfig->containsKey(sIniSectionName, "WRITE_FILE")) 
	{
    if (pConfig->getValue(
          sIniSectionName, "WRITE_FILE", lString)) 
		{
			mIOFilenameWrite = lString;
    }   
  }
  if (pConfig->containsKey(sIniSectionName, "SEND_EOF"))
  {
	  if (pConfig->getValue(sIniSectionName, "SEND_EOF", lString)) {
		  mSendEof = lString == "true" ? true : false;
	  }
  }
}

CFileProtocolEngine::~CFileProtocolEngine()
{
}

bool CFileProtocolEngine::open()
{
	mIOStreamRead.open(mIOFilenameRead.c_str(), std::ios::in | std::ios::binary);
	mIOStreamWrite.open(mIOFilenameWrite.c_str(),std::ios::out | std::ios::binary | std::ios::trunc);

	// determine filesize
	mIOStreamRead.seekg(0, std::ios::end);
	mBytesToRead = mIOStreamRead.tellg();
	mIOStreamRead.seekg(0, std::ios::beg);

	mBytesToRead = mBytesToRead < 0 ? 0 : mBytesToRead;
	mAvailableReadBytes = mBytesToRead + LENGTH_FIELD_LENGTH; // for available() methode
	printl(0 /* LT_STDERR */, "file_protocol_engine.cxx", "Payload length to send: %d \n",
			mBytesToRead);
	mBytesToRead = ::htonl(mBytesToRead);

	// split filesize into bytes 
	for(int lCnt = 0; lCnt < LENGTH_FIELD_LENGTH; ++lCnt)
	{
		mByteLengthBufferRead[lCnt] = mBytesToRead & 0x00FF;
		mBytesToRead >>= 8;
	}

	return true; // FIXXXME: default value (everything ok)
}

void CFileProtocolEngine::close()
{
	if(mIOStreamRead.is_open())
	{
		mIOStreamRead.close();
		printl(0 /* LT_STDERR */, "file_protocol_engine.cxx", "Read truncated. Closing input file. \n");
	}
	if(mIOStreamWrite.is_open())
	{
		mIOStreamWrite.close();
		printl(0 /* LT_STDERR */, "file_protocol_engine.cxx",
				"Receive truncated: %d bytes missing. Closing output file. \n",
				mBytesToReceive);
	}
}

NStegIO::EReadState CFileProtocolEngine::read(unsigned char& pReadValue)
{
	if(mAvailableReadBytes > 0) {
		mAvailableReadBytes--;
	}

	if(mLengthCntRead < LENGTH_FIELD_LENGTH)
	{
		pReadValue = mByteLengthBufferRead[mLengthCntRead];
		++mLengthCntRead;
		//printl(0 /* LT_STDERR */, "file_protocol_engine.cxx", "Reading byte (length): 0x%02X \n",
		//		pReadValue);
		return NStegIO::READ_STATE_DATA;
	}
	else
	{
		if(mIOStreamRead.is_open())
		{
			char lReadValue = 0;
			pReadValue = 0;
			if(mIOStreamRead.good())
			{
				if(mIOStreamRead.read(&lReadValue,1) > 0)
				{
					pReadValue = lReadValue;
					//printl(0 /* LT_STDERR */, "file_protocol_engine.cxx", "Reading byte (data): 0x%02X \n",
					//		pReadValue);
					return NStegIO::READ_STATE_DATA;
				}
				else
				{
					if(mIOStreamRead.eof())
					{
						printl(0 /* LT_STDERR */, "file_protocol_engine.cxx", "Payload sent successfully. \n");
						mIOStreamRead.close();
						return mSendEof ? NStegIO::READ_STATE_EOF : NStegIO::READ_STATE_NODATA;
					}
				}
			}
			else if (mSendEof) {
				printl(0 /* LT_STDERR */, "file_protocol_engine.cxx", "Input stream error. \n");
				mIOStreamRead.close();
				return NStegIO::READ_STATE_ERROR;
			}
		}
	}
	return mSendEof ? NStegIO::READ_STATE_EOF : NStegIO::READ_STATE_NODATA;
}

void CFileProtocolEngine::write(unsigned char pWriteValue)
{
	if(mLengthCntWrite < LENGTH_FIELD_LENGTH)
	{
		printl(0 /* LT_STDERR */, "file_protocol_engine.cxx", "Receiving byte (length): 0x%02X \n",
				pWriteValue);
		mByteLengthBufferWrite[mLengthCntWrite] = pWriteValue;
		++mLengthCntWrite;
		if(mLengthCntWrite == LENGTH_FIELD_LENGTH)
		{
			for(int lCnt = LENGTH_FIELD_LENGTH - 1; lCnt >= 0; --lCnt)
			{
				mBytesToReceive <<= 8;
				mBytesToReceive = mByteLengthBufferWrite[lCnt] + mBytesToReceive;
			}
			mBytesToReceive = ::ntohl(mBytesToReceive);
			printl(0 /* LT_STDERR */, "file_protocol_engine.cxx", "Payload length to receive: %d \n",
					mBytesToReceive);
			if(0 >= mBytesToReceive)
			{
				printl(0 /* LT_STDERR */, "file_protocol_engine.cxx", "No payload to receive. \n");
				mIOStreamWrite.close();
			}
		}
	}
	else
	{
		if(mIOStreamWrite.is_open())
		{
			if(0 < mBytesToReceive)
			{
				//printl(0 /* LT_STDERR */, "file_protocol_engine.cxx", "Receiving byte (data): 0x%02X \n",
				//		pWriteValue);
				char lWriteValue = pWriteValue;

				mIOStreamWrite.write(&lWriteValue,1);
				--mBytesToReceive;
			}
			if(0 >= mBytesToReceive)
			{
				printl(0 /* LT_STDERR */, "file_protocol_engine.cxx", "Payload received successfully. \n");
				mIOStreamWrite.close();
			}
		}
	}
}

int CFileProtocolEngine::available()
{
	// return the rest of the filesize ( + special 4 byte) which can be read
	return mAvailableReadBytes;
}

